/*
 * CKernel.cpp
 *
 *  Created on: 29.11.2011
 *      Author: Kirill Blinov
 *      Copyright: LGPL
 */

#include "CKernel.h"
#include "../Constants.h"
#include "../StructSource.h"
#include "SDL/SDL.h"

#include <boost/thread.hpp>
#include <boost/bind.hpp>

#include "GUI/GUIInit.h"
#include "Visualisator/CVisualisator.h"
#include "Dynamic System/CDynamicEngine.h"

const char GUI_TEMPLATES_PATH[] = "./ShablonsGUI/Shablon.txt";

CKernel* CKernel::Kernel = NULL;

CKernel::CKernel() : currentGameWorld(NULL), mainLoopControl(true), visualisationProxy(NULL)
{
	inputMessageQueue = new TThreadSafeQueue<INPUT_MESSAGE*>( INPUT_MESSAGE_QUEUE_SIZE );
	uiMessageQueue = new TThreadSafeQueue<UI_MESSAGE*>( INPUT_MESSAGE_QUEUE_SIZE );
}

CKernel::~CKernel()
{
	CVisualisator  *Visualisator = CVisualisator::GetVisualisator();
	Visualisator->SetSceneProxy( NULL );

	CManagerGUI *GuiManager = CManagerGUI::GetInstance();
	GuiManager->SetMessageQueue( NULL );
	delete uiMessageQueue;

	// TODO: It can be dangerous. We need to remove this queue from Visualisator first
	// or may be just synchronize it usage.
	delete inputMessageQueue;

	delete currentGameWorld;
	delete visualisationProxy;
	delete gameLogic;
}

void CKernel::KernelMainLoop()
{
	CResourceWarehouse* ResourceWarehouse = ResourceOwner::GetResourceWarehouse();
	const boost::posix_time::milliseconds TargetCicleDuration = 1000 / RENDER_FPS * 2;
	const boost::posix_time::milliseconds CicleDurationThreashold =  TargetCicleDuration *
																	 CICLE_DURATION_THREASHOLD_PERCENT /
			                                                         100;
	boost::system_time FrameStarted = boost::get_system_time();

	while ( mainLoopControl )
	{
		boost::system_time CurrentFrameDuration = boost::get_system_time() - FrameStarted;
		if ( CurrentFrameDuration < TargetCicleDuration - CicleDurationThreashold )
		{
			boost::system_time SleepDuration = TargetCicleDuration - CurrentFrameDuration;
			boost::this_thread::sleep( SleepDuration );
		}
		FrameStarted = boost::get_system_time();

		//Process messages
		INPUT_MESSAGE* InputMessage;
		while ( inputMessageQueue->Receive( &InputMessage ) )
		{
			gameLogic->ProcessInputMessage( *InputMessage );
			delete InputMessage;

			mainLoopControl = gameLogic->OnKernelFrame();
			if ( false ==  mainLoopControl )
			{
				break;
			}
		}

		gameLogic->OnKernelFrame();
	}
}

bool
CKernel::ProcessInputMessage( const INPUT_MESSAGE* InputMessage )
{
	bool GoOnWorking = true;
	static USHORT PrevLeftButtonState = MOUSE_KEY_UP;

	if ( InputMessage != NULL )
	{
		if ( InputMessage->terminateApplication )
		{
			GoOnWorking = false;
		}

		if ( ( InputMessage->mouseDelta.x != 0 ) ||
			 ( InputMessage->mouseDelta.y != 0 ) ||
			 ( PrevLeftButtonState != InputMessage->mouseKeys[MOUSE_BUTTON_LEFT] ) )
		{
			glm::vec2        LogicMousePosition;
			CManagerGUI*     GuiManager = CManagerGUI::GetInstance();
			LogicMousePosition.x = float(InputMessage->mousePosition.x) / float(WINDOW_WIDTH);
			LogicMousePosition.y = ( 1.0f - float(InputMessage->mousePosition.y) / float(WINDOW_HEIGHT) );
			PrevLeftButtonState = InputMessage->mouseKeys[MOUSE_BUTTON_LEFT];
			GuiManager->EventMouse( InputMessage->mouseKeys[MOUSE_BUTTON_LEFT], LogicMousePosition );
		}

	}

	return GoOnWorking;
}

bool CKernel::InitEngine( const std::string GuiTemplate,
						  const std::string GuiMap,
						  const std::string EffectsConfig,
						  const std::string UnitsConfig,
						  const std::string WorldConfig,
						  const SCREEN_DIMENSION &Window )
{
	bool Result = false;
	//Setup dimension
	CVisualisator*   Visualisator = CVisualisator::GetVisualisator();
	CManagerGUI*     GuiManager = CManagerGUI::GetInstance();
	CResourceLoader* ResourceLoader = ResourceOwner::GetResourceLoader();

	//Init window
	Visualisator->SetDimension( Window );
	Visualisator->ConfigVisualisation( RENDER_FPS );

	//-------------GUI---------------------------
	//CManagerGUI Manager;
	Result = GuiManager->Init( GuiTemplate.c_str(), GuiMap.c_str() );
	GuiManager->SetMessageQueue( uiMessageQueue );

	CCheckBox* CheckBox1;

	CheckBox1 = GuiManager->Shablon->ShablonCheckBox[0];
	CheckBox1 = new CCheckBox( *( CheckBox1 ) );
	CheckBox1->SetName("CheckBox1");
	CheckBox1->SetPosition( glm::vec2( 0.25, 0.1 ) );

	//CTreeIterator* Iter = GuiManager.OwnerIterator->GetIterator();
	GuiManager->AddControl("", CheckBox1);

	//--------------Load game objects-------------------------------
	if ( Result )
	{
		Result = ResourceLoader->LoadShadersFromFile( EffectsConfig );
	}

	if ( Result )
	{
		Result = ResourceLoader->LoadGameObjectsFromFile( UnitsConfig );
	}

	if ( Result )
	{
		currentGameWorld = ResourceLoader->LoadGameWorld( WorldConfig );
		Result = ( currentGameWorld != NULL );
	}

	return Result;
}

bool CKernel::LoadWorld( const std::string WorldConfig )
{
	delete currentGameWorld;

	CResourceLoader* ResourceLoader = ResourceOwner::GetResourceLoader();
	currentGameWorld = ResourceLoader->LoadGameWorld( WorldConfig );

	return ( currentGameWorld != NULL );
}

void
CKernel::RunEngine()
{
	boost::thread *RenderRun;

	mainLoopControl = true;
	RenderRun = new boost::thread( boost::bind( CKernel::KernelMainLoop, this ) );

	//Start animation
	CVisualisator  *Visualisator = CVisualisator::GetVisualisator();
	CDynamicEngine *EngineInstance = CDynamicEngine::GetEngine();

	//Just send empty message to start engine
	EngineInstance->PushMessage( NULL );

	//Start rendering
	visualisationProxy = new CSceneVisualisationProxy( currentGameWorld->GetWorldUnitsIterator(), &mainCamera );
	Visualisator->StartVisualisation( visualisationProxy, inputMessageQueue );

    RenderRun->join();
	//After rendering main loop is broken stops everything
	CDynamicEngine::DeleteEngine();
}

void
CKernel::KillEngine()
{
	mainLoopControl = false;
}
